/*
>> PixelFilterOp.s
>>
>> Pixel filter operation skeleton, allowing buffer and scalar inputs,
>> and buffer outputs.
>>
>> Author: Wouter Caarls
>> Date:   14-06-2006
*/

#include "ilpstrategies.h"

void push()
{
  _outp++;

  if (_outp == _stride)
  {
    int _ii;

    for (_ii=0; _ii < arguments; _ii++)
      if (argcode[_ii].argtype == ARG_STREAM_OUT)
      {
        scuBufferReleaseAllocated((@argcode[_ii].argid).buffer, _stride*sizeof(argcode[_ii].datatype));
        scuBufferAllocate((@argcode[_ii].argid).buffer, (void**)&@argcode[_ii].id,
                          _stride*sizeof(argcode[_ii].datatype), SCU_BUF_BLOCK_ALL);
        _outp = 0;
      }
  }
}

void PixelFilterOp(argcode_t *argcode, int arguments, code body)
{
  int _fi=-1, _ii, _stride, _outp = 0;
  int _hpixels;

  for (_ii=0; _ii < arguments; _ii++)
  {
    argcode[_ii].state.buffer = 1;
    argcode[_ii].state.delay = 0;
    argcode[_ii].state.state = 0;

    /* Allocate arguments */
    if (argcode[_ii].argtype == ARG_STREAM_IN)
    {
      /* Adjust first input */
      if (_fi == -1)
        _fi = _ii;
       
      @declare(addrpointer(argcode[_ii].datatype), argcode[_ii].id);
      body = replace(argcode[_ii].id, `&($@argcode[_ii].id)[_ii]`, body);
    }
    else if (argcode[_ii].argtype == ARG_STREAM_OUT)
    {
      @declare(addrpointer(argcode[_ii].datatype), argcode[_ii].id);
      body = replace(argcode[_ii].id, `&($@argcode[_ii].id)[_outp]`, body);
    }

    else
    {   
      /* Non-streams must be initialised */
      @declare(addrpointer(argcode[_ii].datatype), argcode[_ii].id);
      if (@dataclass(argcode[_ii].datatype) == DATACLASS_INTEGER)   
        @argcode[_ii].id = &(@argcode[_ii].argid).data.d;
      else if (@dataclass(argcode[_ii].datatype) == DATACLASS_FLOAT)
        @argcode[_ii].id = &(@argcode[_ii].argid).data.f;
       else
        @argcode[_ii].id = cast(addpointer(argcode[_ii].datatype), (@argcode[_ii].argid).data.b);
    }
  }  

  _stride = scuBufferGetStride((@argcode[_fi].argid).buffer);

  /* Initial allocation */
  for (_ii=0; _ii < arguments; _ii++)
    if (argcode[_ii].argtype == ARG_STREAM_OUT)
      scuBufferAllocate((@argcode[_ii].argid).buffer, (void**)&@argcode[_ii].id,
                          _stride*sizeof(argcode[_ii].datatype), SCU_BUF_BLOCK_ALL);

  while (_hpixels = scuBufferPeek((@argcode[_fi].argid).buffer, (@argcode[_fi].argid).reader,
                      (void**)&(@argcode[_fi].id), _stride*sizeof(argcode[_fi].datatype), SCU_BUF_BLOCK_ALL))
  {
    int _ii;

    _hpixels = _hpixels / sizeof(argcode[_fi].datatype);

    /* Peek all inputs */
    for (_ii=0; _ii < arguments; _ii++)
      if (_ii != _fi && argcode[_ii].argtype == ARG_STREAM_IN)
        scuBufferPeek((@argcode[_ii].argid).buffer, (@argcode[_ii].argid).reader,
                      (void**)&(@argcode[_ii].id), _hpixels*sizeof(argcode[_ii].datatype), SCU_BUF_BLOCK_ALL);

    for (_ii=0; _ii < _hpixels; _ii++)
      @body;
     
    /* Release stuff */
    for (_ii=0; _ii < arguments; _ii++)
      if (argcode[_ii].argtype == ARG_STREAM_IN)
        scuBufferReleasePeeked((@argcode[_ii].argid).buffer, (@argcode[_ii].argid).reader,
                               _hpixels*sizeof(argcode[_ii].datatype));
  }  
  
  /* Release straggling outputs */
  for (_ii=0; _ii < arguments; _ii++)
    if (argcode[_ii].argtype == ARG_STREAM_OUT)
      scuBufferReleaseAllocated((@argcode[_ii].argid).buffer, _outp*sizeof(argcode[_ii].datatype));

  finalizearguments_direct(argcode, arguments);

  /* Set properties */
  propEnvReset();
  propEnv("operation", operation);
  propEnvBack();
 
  for (_ii=0; _ii < arguments; _ii++)
  {
    propEnv("argument", @idtostring(makeid(`id`, _ii)));
      propSeti("id", _ii);
      propEnv("target", target);

      propSeti("buffer", argcode[_ii].state.buffer);
      propSeti("state", argcode[_ii].state.state);
      propSeti("delay", argcode[_ii].state.delay);

      propSeti("elementsize", sizeof(argcode[_ii].datatype));

      if (argcode[_ii].argtype == ARG_STREAM_IN || argcode[_ii].argtype == ARG_STREAM_OUT)
        propSeti("chunksize", 1024);
      else
        propSeti("chunksize", 1);

      propSets("timedist", "regular");

      propEnvBack();
    propEnvBack();
  }
}
